package main

import (
	"fmt"
)

/**
像PHP一样遍历一个数组
*/
func mapDump(m map[string]string) {
	// 达到遍历一个PHP数组一样效果
	fmt.Println("Array(")
	for k, v := range m {
		fmt.Printf("\t\"%v\"=>\"%v\"\n", k, v)
	}
	fmt.Println(")")
}

func main() {
	/**
	【map类型建立方式一】
	- 定义单维：map[K]V
	- 定义多维(复合map)：map[K1]map[K2]V
	*/
	m := map[string]string{
		"name":    "ccmouse",
		"course":  "golang",
		"site":    "imooc",
		"quality": "notbad",
	}
	fmt.Println(m) // map[course:golang name:ccmouse quality:notbad site:imooc]
	var m1 map[string]int
	fmt.Println(m1)        // map[] m1 == nil
	fmt.Println(m1 == nil) // 打印true，所以map的零值是nil

	/**
	【map建立方式二】
	- 使用make函数
	*/
	m2 := make(map[string]int) // m2 == empty map
	fmt.Println(m2)            // map[]
	fmt.Println(m2 == nil)     // false，map的零值是nil，但是通过make出来的一个空map它并不是map的零值

	/**
	【map的遍历】
	- 可以使用range去遍历
	- 你会发现每次遍历的时候，里面par的便利顺序会不一样，所以map它的key是无序的，它是一个hash的map
	*/
	mapDump(m)
	mapDump(m)

	/**
	【获取map的值】
	- 可以直接通过key的键名拿到键值
	- 如果这个key不存在，那么会拿到对应value的零值，string的零值是空字符，那么m["a"]取出来就是""
	- go语言不怕你的变量不初始化，因为每个变量只要定义了就一定有零值（Zero Value），所以不用担心这种没赋值的问题，它会把零值返回给你
	*/
	coureName := m["course"]
	fmt.Println(coureName)

	/**
	【如果判断一个key是否存在】
	- 可以使用赋值的第二个参数作为是否拥有这个key，值为bool值
	- 利用这个第二个参数，可以使用if来简化对应的操作，因为go的if能像其他语言的for一样可以初始化
	*/
	name, isExists1 := m["name"]
	fmt.Println(name, isExists1) // ccmouse true
	couname, isExists2 := m["aaa"]
	fmt.Println(couname, isExists2) //  false

	if name1, ok := m["name"]; ok {
		fmt.Println(name1)
	} else {
		fmt.Println("key does not exists")
	}

	/**
	【如何删除一个map元素】
	- 利用内建的delete函数
	*/
	nameValue, isExists := m["name"]
	fmt.Println(nameValue, isExists)
	delete(m, "name")
	nameValue, isExists = m["name"]
	fmt.Println(nameValue, isExists)

	/**
	小结：
	（1）Map的操作
	- 创建：make(map[string]int)
	- 获取元素：m[key]
	- key不存在时，获取的Value类型的初始值（零值）
	- 用value,ok:=m[key]来判断是否存在key
	（2）Map的遍历
	- 使用range遍历key，或者遍历key，value对
	- 不保证遍历顺序，如需顺序，需手动对key排序（讲所有key拿到一个slice里面去，slice是可以做排序的，排完之后呢，再去利用已经排序好的key去遍历这个Map）
	- 使用len来获得元素的个数
	（3）Map的key说明
	提问：什么样的类型能作为map的key呢？
	- map使用哈希表，必须可以比较相等
	- 除了slice、map、function的内建类型都可以作为key（因为slice、map、function我们没办法判断相等）
	- Struct类型不包含上述字段，也可以作为key（自定义类型也能作为key，只要不包含上述字段），它里面有一些field，只要field里面没有slice、map、function，那么这个structure也能作为key，这个是怎么检查的呢？编译的时候检查的，它会检查你struct上面含不含有上面说的那三种内建类型
	（4）map的使用简单但使用途径非常广泛
	*/

}
